package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.client.CartFeignClient;
import com.atguigu.gmall.common.config.constant.MqConst;
import com.atguigu.gmall.common.config.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.PaymentType;
import com.atguigu.gmall.model.enums.PaymentWay;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author atguigu-mqx
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper , OrderInfo> implements OrderService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${ware.url}")
    private String wareUrl;  // http://localhost:9001

    @Autowired
    private RabbitService rabbitService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        //  编写insert 业务逻辑！
        //  插入数据到orderInfo表
        //  细节：total_amount，order_status,user_id,out_trade_no,trade_body,operate_time,expire_time,process_status
        orderInfo.sumTotalAmount();
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + "" + new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        orderInfo.setTradeBody("结婚买房买车买手机.");
        //  将所有的商品名称进行拼接！
        orderInfo.setOperateTime(new Date());
        //  自己赋值支付方式！
        orderInfo.setPaymentWay(PaymentWay.ONLINE.name());
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE,1);
        orderInfo.setExpireTime(calendar.getTime()); // +1天
        //  在订单的进度状态中能够获取到订单状态！
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        orderInfoMapper.insert(orderInfo);
        //  删除：购物车数据： List<skuId> skuIdList  for() { hdel key skuId }
        //  插入数据到orderDetail表
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        orderDetailList.stream().forEach((orderDetail -> {
            //  删除购物车数据
            //  赋值订单Id
            orderDetail.setOrderId(orderInfo.getId());
            orderDetailMapper.insert(orderDetail);
        }));

        //  删除购物车数据！
        //  cartFeignClient.delCart(orderInfo.getUserId().toString());
        Long orderId = orderInfo.getId();

        //  保存订单： 发送一个消息：
        //  this.rabbitService.sendMessage(); // 发送的普通消息，并发延迟队列！
        //  发送消息的内容是根据消费者决定！ 测试 3秒钟！
        this.rabbitService.sendDelayMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,MqConst.ROUTING_ORDER_CANCEL,orderId,MqConst.DELAY_TIME);

        //  返回数据
        return orderId;
    }

    @Override
    public String getTradeNo(String userId) {
        //  这个就是流水号！
        String tradeNo = UUID.randomUUID().toString();
        //  定义Key
        String key ="tradeNoKey:"+userId;
        //  存储到缓存中：
        this.redisTemplate.opsForValue().set(key,tradeNo);

        return tradeNo;

    }

    @Override
    public Boolean checkTradeNo(String tradeNo, String userId) {
        //  获取到缓存的流水号
        //  定义Key
        String key ="tradeNoKey:"+userId;
        String redisTradeNo = (String) this.redisTemplate.opsForValue().get(key);
        //  返回比较结果
        return tradeNo.equals(redisTradeNo);
    }

    @Override
    public void delTradeNo(String userId) {
        //  定义Key
        String key ="tradeNoKey:"+userId;
        this.redisTemplate.delete(key);
    }

    @Override
    public Boolean checkStock(Long skuId, Integer skuNum) {
        //  http://localhost:9001/hasStock/?skuId=10221&num=2
        String result = HttpClientUtil.doGet(wareUrl + "/hasStock/?skuId=" + skuId + "&num=" + skuNum);
        //  返回结果
        return "1".equals(result);
    }

    @Override
    public IPage<OrderInfo> getPage(Page<OrderInfo> pageParam, String userId) {

        //  调用订单Mapper：
        //  显示的时候，需要显示当前这个订单下的订单明细！
        //  orderInfoMapper.selectPage(pageParam,new QueryWrapper<OrderInfo>().eq("user_id",userId));
        IPage<OrderInfo> page =  orderInfoMapper.selectPageByUserId(pageParam,userId);
        //  获取显示用的订单状态！
        page.getRecords().stream().forEach((orderInfo)->{
            orderInfo.setOrderStatusName(OrderStatus.getStatusNameByStatus(orderInfo.getOrderStatus()));
        });
        //  返回数据
        return page;
    }

    @Override
    public void execExpiredOrder(Long orderId) {
        //  更新订单状态本质： 执行 update 语句！
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        //        orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
        //        orderInfo.setUpdateTime(new Date());
        //        orderInfoMapper.updateById(orderInfo);
        //  试想 ：如果以后支付成功了，那么订单的状态，进程状态应该是： PAID 将上述方法进行整合！
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);
//        this.updateOrderStatus(orderId,ProcessStatus.PAID);
//        this.updateOrderStatus(orderId,ProcessStatus.SPLIT);

        //  发送消息：
        this.rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
    }

    @Override
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setProcessStatus(processStatus.name());
        //  可以从进程状态中获取到订单状态！
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfo.setUpdateTime(new Date());
        orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //  获取orderInfo
        OrderInfo orderInfo = this.orderInfoMapper.selectById(orderId);
        //  后续需要用到订单明细！
        if (orderInfo!=null){
            QueryWrapper<OrderDetail> orderDetailQueryWrapper = new QueryWrapper<>();
            orderDetailQueryWrapper.eq("order_id",orderId);
            List<OrderDetail> orderDetailList = this.orderDetailMapper.selectList(orderDetailQueryWrapper);

            //  赋值
            orderInfo.setOrderDetailList(orderDetailList);
        }
        return orderInfo;
    }

    @Override
    public void sendOrderStatus(Long orderId) {
        //  发送消息给库存了
        this.updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);
        //  参考库存系统接口！   发送是Json 字符串！
        //  根据订单Id 来获取到订单orderInfo 对象，
        OrderInfo orderInfo = this.getOrderInfo(orderId);

        //  将orderInfo 中的部分字段转换为Map 集合
        Map map = this.initWare(orderInfo);

        //  将map 转换为Json 字符串，发送给库存！
        this.rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(map));
    }

    public Map initWare(OrderInfo orderInfo) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("orderId", orderInfo.getId());
        map.put("consignee", orderInfo.getConsignee());
        map.put("consigneeTel", orderInfo.getConsigneeTel());
        map.put("orderComment", orderInfo.getOrderComment());
        map.put("orderBody", orderInfo.getTradeBody());
        map.put("deliveryAddress", orderInfo.getDeliveryAddress());
        map.put("paymentWay", "2");
        map.put("wareId", orderInfo.getWareId());// 仓库Id ，减库存拆单时需要使用！
        //  第一种：
        //        List<Map> list = new ArrayList<>();
        //        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //        for (OrderDetail orderDetail : orderDetailList) {
        //            //  声明一个map 集合
        //            HashMap<String, Object> detailMap = new HashMap<>();
        //            detailMap.put("skuId",orderDetail.getSkuId());
        //            detailMap.put("skuName",orderDetail.getSkuName());
        //            detailMap.put("skuNum",orderDetail.getSkuNum());
        //            //  添加到这个map;
        //            list.add(detailMap);
        //        }

        //  第二种写法：
        List<HashMap<String, Object>> list = orderInfo.getOrderDetailList().stream().map(orderDetail -> {
            //  声明一个map 集合
            HashMap<String, Object> detailMap = new HashMap<>();
            detailMap.put("skuId", orderDetail.getSkuId());
            detailMap.put("skuName", orderDetail.getSkuName());
            detailMap.put("skuNum", orderDetail.getSkuNum());
            return detailMap;
        }).collect(Collectors.toList());

        map.put("details",list); // details:[{skuId:101,skuNum:1,skuName:’小米手64G’},{skuId:201,skuNum:1,skuName:’索尼耳机’}]
        //  返回map
        return map;
    }

    @Override
    public List<OrderInfo> orderSplit(String orderId, String wareSkuMap) {
        /*
        1.  知道谁被拆单，先获取到原始订单是谁！
        2.  wareSkuMap  [{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}] 转换为java 操作的对象
        3.  创建新的子订单，并给子订单赋值
        4.  子订单保存到数据库orderInfo
        5.  将新的子订单添加到子订单集合
        6.  修改原始订单状态
         */
        List<OrderInfo> subOrderInfoList = new ArrayList<>();

        OrderInfo orderInfoOrigin = this.getOrderInfo(Long.parseLong(orderId));
        List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
        //  判断
        if (!CollectionUtils.isEmpty(mapList)){
            //  循环遍历
            for (Map map : mapList) {
                String wareId = (String) map.get("wareId");
                List<String> skuIdList = (List<String>) map.get("skuIds");
                //  创建子订单对象
                OrderInfo subOrderInfo = new OrderInfo();
                //  属性拷贝
                BeanUtils.copyProperties(orderInfoOrigin,subOrderInfo);
                //  需要做细节处理：
                subOrderInfo.setId(null);
                //  父订单Id
                subOrderInfo.setParentOrderId(Long.parseLong(orderId));
                //  赋值仓库Id
                subOrderInfo.setWareId(wareId);
                //  子订单金额计算！
                //  声明一个子订单明细集合：
                List<OrderDetail> orderDetails = new ArrayList<>();
                List<OrderDetail> orderDetailList = orderInfoOrigin.getOrderDetailList();
                for (OrderDetail orderDetail : orderDetailList) {
                    //  找到属于子订单的订单明细集合
                    for (String skuId : skuIdList) {
                        if (orderDetail.getSkuId()== Long.parseLong(skuId)){
                            orderDetails.add(orderDetail);
                        }
                    }
                }

                //  计算子订单的价格：
                subOrderInfo.setOrderDetailList(orderDetails);
                subOrderInfo.sumTotalAmount();
                //  保存子订单数据
                this.saveOrderInfo(subOrderInfo);
                //  将子订单添加到集合中！
                subOrderInfoList.add(subOrderInfo);
            }
        }
        //  修改原始订单状态
        this.updateOrderStatus(Long.parseLong(orderId),ProcessStatus.SPLIT);
        return subOrderInfoList;
    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {
        //  关闭订单orderInfo
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);
        //  判断
        if ("2".equals(flag)){
            //  发送消息：
            this.rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        }

    }

    @Override
    public void updateCommnetStatus(Long orderId) {
        OrderInfo orderInfo = this.getById(orderId);
        orderInfo.setOrderStatus(ProcessStatus.COMMNET.getOrderStatus().name());
        orderInfo.setProcessStatus(ProcessStatus.COMMNET.name());
        this.updateById(orderInfo);
    }


}
